﻿// <copyright file="AppNotificationPostDataBuilder.cs" company="Robert Venables">
// Copyright (c) 2009 Robert Venables.
// </copyright>
// <author>Robert Venables</author>
// <email>rob@rvenables.com</email>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;

namespace VNotify
{
    /// <summary>
    /// Constructs a complete block of properly formatted post data
    /// from an AppNotification object and an APIKey.
    /// </summary>
    internal class AppNotificationPostDataBuilder
    {
        /// <summary>
        /// All special case conversions that override the default behavior
        /// when converting AppNotification fields to post data.
        /// </summary>
        private static Dictionary<string, Func<object, string>> specialConverters =
            new Dictionary<string, Func<object, string>>()
            {
                // Converts sound enum value to core audio format filename
                {"sound", value => ((int)value != 0) ? ((int)value).ToString(CultureInfo.CurrentCulture) + ".caf" : string.Empty}
            };

        /// <summary>
        /// The API Key to encode into the post data.
        /// </summary>
        private string apiKey = string.Empty;

        /// <summary>
        /// The set of final post values which represent the AppNotification and API Key.
        /// </summary>
        private Dictionary<string, string> notificationPostValues = new Dictionary<string, string>();

        /// <summary>
        /// Initializes a new instance of the AppNotificationPostDataBuilder class with
        /// the source AppNotification to gather data from (and the required APIKey).
        /// </summary>
        /// <param name="source">The AppNotification to get most of the post values from.</param>
        /// <param name="apiKey">The API key to be included in the post data.</param>
        public AppNotificationPostDataBuilder(AppNotification source, string apiKey)
        {
            if (apiKey == null || string.IsNullOrEmpty(apiKey) || apiKey.Length < 4)
                throw new ArgumentException("Invalid API Key!");

            this.apiKey = apiKey;
            this.notificationPostValues = GetPostValues(source);
        }

        /// <summary>
        /// Gets the URL-encoded post data for the supplied AppNotification and APIKey.
        /// </summary>
        public string EncodedPostValues
        {
            get
            {
                List<string> encodedPairs = new List<string>();

                // Encodes notification key/value pairs
                Func<string, string, string> encodeNoficationPair
                    = (KeyName, Value) =>
                        "notification[" + HttpUtility.UrlEncode(KeyName) + "]=" +
                            HttpUtility.UrlEncode(Value);

                // Add each notification post value
                this.notificationPostValues.Keys
                    .ToList()
                    .ForEach(k => encodedPairs.Add(
                        encodeNoficationPair(k, this.notificationPostValues[k])));

                // Add API Key
                encodedPairs.Add("user_credentials=" + this.apiKey);

                return string.Join("&", encodedPairs.ToArray());
            }
        }

        /// <summary>
        /// Converts a given AppNotification into a string based 
        /// dictionary of query key-value pairs.
        /// </summary>
        /// <param name="notification">The source notification to convert.</param>
        /// <returns>A dictionary of key-value pairs representing the AppNotification object</returns>
        private static Dictionary<string, string> GetPostValues(AppNotification notification)
        {
            if (notification == null)
                throw new ArgumentNullException("notification");

            var workingQueryValues = new Dictionary<string, string>();

            AddNotificationValue(workingQueryValues, "message", notification.Message);
            AddNotificationValue(workingQueryValues, "long_message", notification.LongMessage);
            AddNotificationValue(workingQueryValues, "title", notification.Title);
            AddNotificationValue(workingQueryValues, "long_message_preview", notification.LongMessagePreview);
            AddNotificationValue(workingQueryValues, "message_level", notification.MessageLevel);
            AddNotificationValue(workingQueryValues, "silent", notification.Silent);
            AddNotificationValue(workingQueryValues, "action_loc_key", notification.NotificationButtonText);
            AddNotificationValue(workingQueryValues, "run_command", notification.RunCommand);
            AddNotificationValue(workingQueryValues, "sound", notification.Sound);

            return workingQueryValues;
        }

        /// <summary>
        /// Adds a string-convertable value to a given Dictionary (of string, string)
        /// If Key is found in static SpecialConverters dictionary, the corresponding
        /// conversion is used. Otherwise, makes use of Convert.ToString(...).
        /// </summary>
        /// <typeparam name="TValue">The type of value to add.</typeparam>
        /// <param name="target">The target dictionary to use.</param>
        /// <param name="key">The key to use when adding Value to Target dictionary.</param>
        /// <param name="value">The value to add to Target dictionary.</param>
        private static void AddNotificationValue<TValue>(Dictionary<string, string> target, string key, TValue value)
        {
            if (target == null)
                throw new ArgumentNullException("target");

            if (String.IsNullOrEmpty(key))
                throw new ArgumentException("Invalid Key!");

            string convertedValue = string.Empty;

            if (specialConverters.Keys.Contains(key))
            {
                convertedValue = specialConverters[key](value);
            }
            else
            {
                convertedValue = Convert.ToString(value, CultureInfo.CurrentCulture);
            }

            if (!String.IsNullOrEmpty(convertedValue))
            {
                target.Add(key, convertedValue);
            }
        }
    }
}
